package org.transaction.test.global_transaction.mybatis.service.impl;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.transaction.test.global_transaction.mybatis.bean.User1;
import org.transaction.test.global_transaction.mybatis.bean.User2;
import org.transaction.test.global_transaction.mybatis.service.TransactionAndThread;
import org.transaction.test.global_transaction.mybatis.service.User1Service;
import org.transaction.test.global_transaction.mybatis.service.User2Service;
import org.transaction.test.global_transaction.mybatis.utils.SqlContext;

import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

@Service
public class TransactionAndThreadImpl implements TransactionAndThread {
	@Autowired
	private User1Service user1Service;
	@Autowired
	private User2Service user2Service;


	@Autowired
	@Qualifier("user1TM") // 指定具体的事务管理器
	private PlatformTransactionManager transactionManager;


	@Autowired
	@Qualifier("user2TM") // 指定具体的事务管理器
	private PlatformTransactionManager transactionManager2;


	@Autowired
	@Qualifier("sqlSessionFactory1")
	private SqlSessionFactory sqlSessionFactory1;




	@Override
	public void truncated() {
		user1Service.truncate();
		user2Service.truncate();
	}

	/**
	 * 1、已验证
	 * 外围方法：开启事务，抛出异常，没有捕获
	 * 内部调用方法：开启事务
	 *
	 * 10个线程进行添加操作，其中一个抛出异常，小明5
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public void transaction_exception_required_thread(){
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		List<Callable<List<Void>>> tasks = new ArrayList<>();
		//10个线程进行添加操作，其中一个抛出异常
		for (int i = 0; i < 10; i++) {
			int finalI = i;
			tasks.add(() -> {
				User1 user1 = new User1();
				user1.setName("小明"+ finalI);
				if(finalI==5) {
					user1Service.addRequiredException(user1);
				}else{
					user1Service.addRequired(user1);
				}
				return null;
			});
		}
		try {
			executorService.invokeAll(tasks);
		} catch (InterruptedException e) {
			System.err.println("线程异常:"+e.getMessage());;
		}finally {
			executorService.shutdown();
		}
	}

	/**
	 * 2、已验证 结果：一个异常，全部线程回滚
	 * 编程式事务管理+ CountDownLatch+ AtomicBoolean + sqlSession 方式来共享事务状态，或者改为单线程实现
	 *
	 * 期望：如果您希望在一个线程抛出异常时，其他线程也停止执行，并且整个事务回滚，
	 * 那么您需要确保所有线程都在同一个事务上下文中运行。
	 * 然而，在多线程环境中，每个线程默认都有自己的事务上下文，
	 * 因此直接使用 @Transactional 注解并不能达到这个效果。
	 * 为了实现这一目标，您可以使用一个共享的标志来控制线程的执行，并在捕获到异常时设置该标志，
	 * 使其他线程能够检测到并提前退出。此外，您还需要手动管理事务的提交和回滚。
	 */
	@Override
	public void transaction_exception_required_thread_02() {
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		CountDownLatch latch = new CountDownLatch(10); // 设置计数器为任务数量
		AtomicBoolean hasException = new AtomicBoolean(false);

		try (SqlSession sqlSession = sqlSessionFactory1.openSession(true)) { // 开启自动提交
			Connection connection = sqlSession.getConnection();
			connection.setAutoCommit(false); // 关闭自动提交

			List<Callable<Void>> tasks = new ArrayList<>();
			for (int i = 0; i < 10; i++) {
				final int finalI = i;
				tasks.add(() -> {
					try {
						User1 user1 = new User1();
						user1.setName("小明" + finalI);
						if (finalI == 5) {
							user1Service.add(user1, sqlSession);
						} else {
							user1Service.add(user1, sqlSession);
						}
					} catch (Exception e) {
						System.err.println("捕获异常：" + e.getMessage());
						hasException.set(true); // 设置异常标志
					} finally {
						latch.countDown(); // 减少计数器
					}
					return null;
				});
			}

			try {
				List<Future<Void>> futures = executorService.invokeAll(tasks);
				latch.await(); // 等待所有任务完成

				for (Future<Void> future : futures) {
					try {
						future.get(); // 确保所有任务没有未处理的异常
					} catch (ExecutionException e) {
						System.err.println("任务执行异常: " + e.getCause().getMessage());
						hasException.set(true); // 设置异常标志
					}
				}

				if (!hasException.get()) {
					// 如果没有任何异常，提交事务
					connection.commit();
				}
			} catch (InterruptedException e) {
				System.err.println(e.getMessage());
			} finally {
				// 如果有异常或者中断，回滚事务
				if (hasException.get() || Thread.currentThread().isInterrupted()) {
					connection.rollback();
				}
				executorService.shutdown();
				connection.close();
			}
		} catch (Exception e) {
			System.err.println("SqlSession 创建失败: " + e.getMessage());
		}
	}

	/**
	 * 3、已验证 结果：一个异常，全部线程回滚
	 * 多线程下：通过sqlSession控制所有线程事务回滚
	 */
	@Override
	public void transaction_exception_required_thread_03() throws SQLException {
		ExecutorService executorService = Executors.newFixedThreadPool(10);

		SqlSession sqlSession = sqlSessionFactory1.openSession(true); // 开启自动提交
		Connection connection = sqlSession.getConnection();
		connection.setAutoCommit(false);// 关闭自动提交

		try {
			List<Callable<Void>> tasks = new ArrayList<>();
			for (int i = 0; i < 10; i++) {
				final int finalI = i;
				tasks.add(() -> {
					try {
						User1 user1 = new User1();
						user1.setName("小明" + finalI);
						if (finalI == 5) {
							user1Service.addException(user1, sqlSession);
						} else {
							user1Service.add(user1, sqlSession);
						}
					} catch (Exception e) {
						System.err.println("捕获异常: " + e.getMessage());
						// 设置手动回滚
						connection.rollback();
					}
					return null;
				});
			}

			List<Future<Void>> futures = executorService.invokeAll(tasks);
			connection.commit();
		} catch (Exception e) {
			connection.rollback();
			System.err.println(e.getMessage());
		} finally {
			executorService.shutdown();
			connection.close();
		}
	}




	/**
	 * 4、已验证  结果：只要有事务管理的异常都回滚，但不影响其他线程的插入，小明1和小明2正常插入
	 *
	 * 外围方法：开启事务，抛出异常
	 * 内部调用方法：开启事务,不开启事务无法回滚，而且需要指定事务管理器
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public void transaction_exception_required_thread_try_catch(){

		ExecutorService executorService = Executors.newFixedThreadPool(10);
		List<Callable<List<Void>>> tasks = new ArrayList<>();
		//10个线程进行添加操作，其中一个抛出异常
		for (int i = 0; i < 10; i++) {
			int finalI = i;
			tasks.add(() -> {
				try {
					User1 user1 = new User1();
					User2 user2 = new User2();
					user1.setName("小明"+ finalI);
					user2.setName("小李"+ finalI);
					if(finalI==1){
						user1Service.addRequired(user1);
					}else if(finalI==2){
						user2Service.addRequired(user2);
					}else if(finalI==5) {
						user1Service.addRequiredException(user1);
					}else{
						user2Service.addRequiredException(user2);
					}
				}catch (Exception e){
					System.err.println("捕获异常："+e.getMessage());
				}
				return null;
			});
		}
		try {
			executorService.invokeAll(tasks);
		} catch (InterruptedException e) {
			System.err.println(e.getMessage());;
		}

	}


	/**
	 * 编程式事务管理 + try-catch
	 * 5、已验证  结果：只要有事务管理的异常都回滚，但不影响其他线程的插入，小明1和小李2正常插入.
	 * 	小明3和小李4 也插入，即使小明4抛出异常也不回滚，小李4没开启事务管理
	 *
	 * 外围方法：开启事务，捕获异常
	 * 内部调用方法：开启事务
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public void transaction_exception_required_thread_try_catch_02() {
		ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

		List<Callable<Void>> tasks = new ArrayList<>();
		for (int i = 0; i < 10; i++) {
			final int finalI = i;
			tasks.add(() -> {
				TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

				try {
					transactionTemplate.execute(new TransactionCallbackWithoutResult() {
						@Override
						protected void doInTransactionWithoutResult(TransactionStatus status) {
							User1 user1 = new User1();
							User2 user2 = new User2();
							user1.setName("小明"+ finalI);
							user2.setName("小李"+ finalI);
							if(finalI==1){
								user1Service.addRequired(user1);
							}else if(finalI==2){
								user2Service.addRequired(user2);
							}else if(finalI==3){
								user1Service.add(user1);
							}else if(finalI==4) {
								user2Service.addException(user2);
							}else if(finalI==5) {
								user1Service.addRequiredException(user1);
							}else{
								user2Service.addRequiredException(user2);
							}
						}

					});
				} catch (Exception e) {
					System.err.println("捕获异常:"+finalI+"：" + e.getMessage());
				}

				return null;
			});
		}

		try {
			List<Future<Void>> futures = executor.invokeAll(tasks);
		} catch (InterruptedException e) {
			System.err.println(e.getMessage());
		} catch (Exception e){
			System.out.println("获取结果异常："+e.getMessage());
		}
		finally {
			executor.shutdown();
		}

	}




}
